

#include "xncan.h"
#include <stdio.h>
#ifdef XNLINUX
#include <unistd.h>
#endif
#include <string.h>
 
CXnCan::CXnCan()
{
	m_bOpenOK = false;	

#ifdef XNWIN
	m_bInitDllOK = false;
	m_iDevtype = VCI_USBCAN2;
	m_dwDevind = 0;

	m_hDllInst = LoadLibraryA("ControlCAN.dll");

	if (m_hDllInst)
	{
		m_pVCI_OpenDevice = (pVCI_OpenDevice)GetProcAddress(m_hDllInst, "VCI_OpenDevice");
		m_pVCI_InitCAN = (pVCI_InitCAN)GetProcAddress(m_hDllInst, "VCI_InitCAN");
		m_pVCI_CloseDevice = (pVCI_CloseDevice)GetProcAddress(m_hDllInst, "VCI_CloseDevice");
		m_pVCI_StartCAN = (pVCI_StartCAN)GetProcAddress(m_hDllInst, "VCI_StartCAN");
		m_pVCI_Receive = (pVCI_Receive)GetProcAddress(m_hDllInst, "VCI_Receive");
		m_pVCI_Transmit = (pVCI_Transmit)GetProcAddress(m_hDllInst, "VCI_Transmit");
		m_pVCI_ReadErrInfo = (pVCI_ReadErrInfo)GetProcAddress(m_hDllInst, "VCI_ReadErrInfo");
		m_pVCI_ClearBuffer = (pVCI_ClearBuffer)GetProcAddress(m_hDllInst, "VCI_ClearBuffer");

		if (!m_pVCI_OpenDevice || !m_pVCI_InitCAN || !m_pVCI_CloseDevice || !m_pVCI_StartCAN || !m_pVCI_Receive || !m_pVCI_Transmit || !m_pVCI_ReadErrInfo || !m_pVCI_ClearBuffer) 
		{
			m_bInitDllOK = false;
		}
		else
		{
			m_bInitDllOK = true;
		}		
	}
#elif XNLINUX
	memset(m_aCanName, 0, 64);
	m_fdSocketCan = -1;
#endif
}

CXnCan::~CXnCan()
{
	closeDevice();

#ifdef XNWIN
	if (m_hDllInst)
	{
		FreeLibrary(m_hDllInst);
		m_hDllInst = 0;
	}	
#endif
}

//  0  - OK
// -10 - Init Dll FAILED 
// -1  - Open device FAILED
// -999  - Other Err

//  Linux
//  0 - OK
// -1 - Param Error
// -2 - pDeviceName 长度不对
// -3 - stop Can FAILED
// -4 - set itrate FAILED
// -5 - set ctrl mode FAILED
// -6 - start can FAILED
// -7 - create socket can FAILED
// -8 - init socket can FAILED
// -9 - bind socket can FAILED
int CXnCan::openDevice(const char *pDeviceName, int iSpeed)
{
	int iRetVal = -999;	

#ifdef XNWIN
	if (!m_bInitDllOK)
	{
		return -10;
	}

	if (0 == strcmp(pDeviceName, "CAN1"))
	{
		m_dwDevind = 0;
	}
	else if (0 == strcmp(pDeviceName, "CAN2"))
	{
		m_dwDevind = 1;
	}
	else
	{
		m_dwDevind = 0;
	}

	if (STATUS_OK != m_pVCI_OpenDevice(m_iDevtype, m_dwDevind, 0)) 
	{
		iRetVal = -1;

		printLastErr();		
	}
	else
	{
		VCI_INIT_CONFIG initConfig;

		initConfig.AccCode = 0xFFFFFFFF;  // 验收码
		initConfig.AccMask = 0xFFFFFFFF;  // 屏蔽位
		initConfig.Filter = 1;            // 单滤波
		initConfig.Mode = 0;              // 正常工作模式

		switch(iSpeed)
		{
		case 5 * 1000:
			initConfig.Timing0=(unsigned char)0xbf;
			initConfig.Timing1=(unsigned char)0xff;
			break;
		case 10 * 1000:
			initConfig.Timing0=(unsigned char)0x31;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 20 * 1000:
			initConfig.Timing0=(unsigned char)0x18;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 40 * 1000:
			initConfig.Timing0=(unsigned char)0x87;
			initConfig.Timing1=(unsigned char)0xff;
			break;
		case 50 * 1000:
			initConfig.Timing0=(unsigned char)0x09;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 80 * 1000:
			initConfig.Timing0=(unsigned char)0x83;
			initConfig.Timing1=(unsigned char)0xff;
			break;
		case 100 * 1000:
			initConfig.Timing0=(unsigned char)0x04;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 125 * 1000:
			initConfig.Timing0=(unsigned char)0x03;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 200 * 1000:
			initConfig.Timing0=(unsigned char)0x81;
			initConfig.Timing1=(unsigned char)0xfa;
			break;
		case 250 * 1000:
			initConfig.Timing0=(unsigned char)0x01;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 400 * 1000:
			initConfig.Timing0=(unsigned char)0x80;
			initConfig.Timing1=(unsigned char)0xfa;
			break;
		case 500 * 1000:
			initConfig.Timing0=(unsigned char)0x00;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		case 666 * 1000:
			initConfig.Timing0=(unsigned char)0x80;
			initConfig.Timing1=(unsigned char)0xb6;
			break;
		case 800 * 1000:
			initConfig.Timing0=(unsigned char)0x00;
			initConfig.Timing1=(unsigned char)0x16;
			break;
		case 1000 * 1000:
			initConfig.Timing0=(unsigned char)0x00;
			initConfig.Timing1=(unsigned char)0x14;
			break;
		default:
			initConfig.Timing0=(unsigned char)0x01;
			initConfig.Timing1=(unsigned char)0x1c;
			break;
		}

		if (STATUS_OK != m_pVCI_InitCAN(m_iDevtype, m_dwDevind, 0, &initConfig))
		{
			m_pVCI_CloseDevice(m_iDevtype, m_dwDevind);

			iRetVal = -1;

			printLastErr();
		}
		else
		{
			if (STATUS_OK != m_pVCI_StartCAN(m_iDevtype, m_dwDevind, 0))
			{
				m_pVCI_CloseDevice(m_iDevtype, m_dwDevind);

				iRetVal = -1;

				printLastErr();
			}
			else
			{
				m_bOpenOK = true;
				iRetVal = 0;
			}
		}	
	}	
#elif XNLINUX	
	if (!pDeviceName || iSpeed < 0)
	{
		return -1;
	}
	
	// some variables
	struct can_ctrlmode cm;
	struct ifreq ifr;
	struct sockaddr_can addr;
	int iLen = strlen(pDeviceName);
	if (iLen <= 0 || iLen >= 63)
	{
		iRetVal = -2;
	}
	else
	{
		memset(m_aCanName, 0, 64);
		memcpy(m_aCanName, pDeviceName, iLen);

		// stop Can
		if(can_do_stop(m_aCanName) < 0)
		{
			iRetVal = -3;
		}
		else
		{
            sleep(2);// 为了can稳定，驱动厂建议停止2S add yangjb 20250117
			if(can_set_bitrate(m_aCanName, iSpeed) < 0)
			{
				iRetVal = -4;
			}
			else
			{
				memset(&cm, 0, sizeof(cm));			
				cm.flags |= CAN_CTRLMODE_3_SAMPLES;				
				cm.mask |= CAN_CTRLMODE_3_SAMPLES;
				
				if(can_set_ctrlmode(m_aCanName, &cm) < 0) 
				{
					iRetVal = -5;
				}
				else
				{
					if(can_do_start(m_aCanName) < 0)
					{
						iRetVal = -6;
					}
					else
					{
						// create socket can
						m_fdSocketCan = socket(PF_CAN, SOCK_RAW, CAN_RAW); /* open socket */
						if (m_fdSocketCan < 0)
						{
							iRetVal = -7;
						}
						else
						{
							if (iLen >= IFNAMSIZ - 1)
							{
								iLen = IFNAMSIZ - 1;
							}
							memcpy(ifr.ifr_name, m_aCanName, iLen);
							ifr.ifr_name[iLen] = '\0';
							ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
							if (!ifr.ifr_ifindex) 
							{
								iRetVal = -8;
							}
							else
							{								 
								 addr.can_family = AF_CAN;
								 addr.can_ifindex = ifr.ifr_ifindex;

								 if (bind(m_fdSocketCan, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
								 {
									 iRetVal = -9;
								 }
								 else
								 {
									 iRetVal = 0;
									 m_bOpenOK = true;
								 }																
							}
						}						
					}
				}
			}
		}
	}
#endif

	return iRetVal;
}

//  0  - OK
// -10 - Init Dll FAILED 

//  0 - OK 
// -1 - FAILED
int CXnCan::closeDevice()
{
#ifdef XNWIN
	if (!m_bInitDllOK)
	{
		return -10;
	}

	if(m_bOpenOK)
	{		
		// clear buffer
		m_pVCI_ClearBuffer(m_iDevtype, m_dwDevind, 0);
		m_pVCI_CloseDevice(m_iDevtype, m_dwDevind);
	}
#elif XNLINUX
	if (m_fdSocketCan < 0)
	{
		return -1;
	}
	
	::close(m_fdSocketCan);
	m_fdSocketCan = -1;	
	
	return can_do_stop(m_aCanName);
#endif

	return 0;
}

// Win
// >= 0 - read 的字节数 (> 100 为扩展帧  < 100 为标准帧)
// -1   - can is not opened 
// -2  - pBuf is NULL
// -3   - Read Failed
// -10  - Init Dll FAILED 

// Linux
// >= 0 - read 的字节数 (> 100 为扩展帧  < 100 为标准帧)
// -1  - can is not opened 
// -2  - pBuf is NULL
// -3  - time out 
// -4  - Read Failed
int CXnCan::readFrame(unsigned int& uiCanId, unsigned char* pBuf, int iTimeout)
{
	if (!m_bOpenOK)
	{
		return -1;
	}

	if(!pBuf) 
	{
		return -2;
	}

	int iRetVal = -999;	

#ifdef XNWIN
	unsigned int uiFrameNum = 0;
	if (!m_bInitDllOK)
	{
		return -10;
	}

	uiFrameNum = m_pVCI_Receive(m_iDevtype, m_dwDevind, 0, canObj, 1, iTimeout);
	if(0xFFFFFFFF == uiFrameNum || 0 == uiFrameNum) // Error || Frame Num = 0
	{
		iRetVal = -3;
	}
	else
	{
		uiCanId = canObj[0].ID;

		iRetVal = canObj[0].DataLen; // 数据长度 字节数

		if (iRetVal >= 0 && iRetVal <= 8)
		{
			memcpy(pBuf, canObj[0].Data, iRetVal);

			if(canObj[0].ExternFlag) // 为扩展帧
			{
				iRetVal += 100;
			}
		}
		else
		{
			iRetVal = -3;
		}
	}

#elif XNLINUX
	struct can_frame frame; 
	if(-1 == m_fdSocketCan) 
	{
		return -1;
	}	

	if(iTimeout >= 0)
	{
		if(select(iTimeout) == 0)  // timeout
		{
			return -3;
		}
	}

	iRetVal = read(m_fdSocketCan, &frame, sizeof(frame));

	if(iRetVal < 0)
	{
		iRetVal = -4;
	}
	else
	{
		uiCanId = frame.can_id;
		memcpy(pBuf, frame.data, frame.can_dlc);
		iRetVal = frame.can_dlc;
		if (frame.can_id & CAN_EFF_FLAG) // 是否为扩展帧
		{
			iRetVal += 100;
		}	
	}
#endif

	return iRetVal;
}

// pBuf 需要准备 sizeof(SCanMutiFrame) *  iFrameNum 的缓存空间
// >= 0 read 的帧数
// -1 - 参数错误
// -2 - 参数帧数错误
// -3 - read can 失败 或 超时
// -999 - Other Err
int CXnCan::readMultiFrame(unsigned char* pBuf, int iFrameNum, int iTimeout)
{
	if (!m_bOpenOK)
	{
		return -1;
	}

	if(!pBuf || iFrameNum < 0)
	{
		return -2;
	}

	int iRetVal = -999;		
	SCanMutiFrame* pCanMutiFrame = 0;

#ifdef XNWIN	
	unsigned int uiFrameNum = 0;
	int iIndex = 0;
	if (!m_bInitDllOK)
	{
		return -10;
	}

	uiFrameNum = m_pVCI_Receive(m_iDevtype, m_dwDevind, 0, canObj, 100, iTimeout);
	if(0xFFFFFFFF == uiFrameNum || 0 == uiFrameNum) // Error || Frame Num = 0
	{
		iRetVal = -3;
	}
	else
	{
		for (iIndex = 0; iIndex < uiFrameNum && iIndex < 100; ++iIndex)
		{
			pCanMutiFrame = (SCanMutiFrame*)(pBuf + sizeof(SCanMutiFrame) * iIndex);

			if(pCanMutiFrame)
			{
				pCanMutiFrame->can_id = canObj[iIndex].ID;

				if(canObj[iIndex].ExternFlag) // 为扩展帧
				{
					pCanMutiFrame->can_id |= 0x80000000U;
				}

				pCanMutiFrame->can_dlc = canObj[iIndex].DataLen;
				if (pCanMutiFrame->can_dlc > 8)
				{
					pCanMutiFrame->can_dlc = 8;
				}

				memcpy(pCanMutiFrame->data, canObj[iIndex].Data, pCanMutiFrame->can_dlc);
			}
		}
		
		iRetVal = iIndex;	
	}

#elif XNLINUX
	struct can_frame frame;
	if(-1 == m_fdSocketCan)  // struct can_frame 为16字节, Linux下只能单帧读取
	{
		return -1;
	}

	if(iTimeout >= 0)
	{
		if(select(iTimeout) == 0)  // timeout
		{
			return -3;
		}
	}

	iRetVal = read(m_fdSocketCan, &(frame), sizeof(struct can_frame));	
	if (iRetVal >= sizeof(struct can_frame))
	{
		pCanMutiFrame = (SCanMutiFrame*)pBuf;
		pCanMutiFrame->can_id = frame.can_id;
		pCanMutiFrame->can_dlc = frame.can_dlc;
		memcpy(pCanMutiFrame->data, frame.data, 8);
	
		iRetVal = 1;
	}
	else
	{
		iRetVal = -4;
	}	
#endif

	return iRetVal;
}

// >0   - write的字节数(Data)
// -1   - can is not opened
// -2   - Write FAILED
// -10  - Init Dll FAILED 
// uaLen  (> 100 为扩展帧  < 100 为标准帧)
int CXnCan::writeFrame(unsigned int uiCanId, unsigned char* pBuf, unsigned char uaLen)
{
	if (!m_bOpenOK)
	{
		return -1;
	}

	int iRetVal = -2;

#ifdef XNWIN
	if (!m_bInitDllOK)
	{
		return -10;
	}

	VCI_CAN_OBJ canObj;

	memset(&canObj, 0, sizeof(VCI_CAN_OBJ));

	canObj.ID = uiCanId;
	canObj.SendType = 1;    // 单次发送
	canObj.RemoteFlag = 0;  // 数据帧 	

	if (uaLen >= 100) // 扩展帧
	{
		canObj.ExternFlag = 1;  // 0 - 标准帧 1 - 扩展帧

		uaLen -= 100;
	}
	else
	{
		canObj.ExternFlag = 0;  // 0 - 标准帧 1 - 扩展帧
	}

	if(uaLen >= 8)
	{
		uaLen = 8;
	}

	canObj.DataLen = uaLen; // 数据长度 字节数
	if (canObj.DataLen > 0)
	{
		memcpy(canObj.Data, pBuf, uaLen);
	}	

	if(1/*返回帧数*/ == m_pVCI_Transmit(m_iDevtype, m_dwDevind, 0, &canObj, 1))
	{
		iRetVal = uaLen;
	}
	else
	{
		iRetVal = -2;
	}
#elif XNLINUX
	struct can_frame frame; 

	if(-1 == m_fdSocketCan) 
	{
		return -1;
	}

	if (uaLen >= 100)
	{
		uiCanId |= CAN_EFF_FLAG;
		uaLen -= 100;
	}
	if (uaLen > 8)
	{
		uaLen = 8;
	}
	frame.can_id = uiCanId;
	frame.can_dlc = uaLen;
	memcpy(frame.data, pBuf, frame.can_dlc);

	iRetVal = write(m_fdSocketCan, &frame, sizeof(frame));

	if (iRetVal < 0)
	{
		iRetVal = -2;
	}
	else
	{
		iRetVal = uaLen;
	}	
#endif
	return iRetVal;
}

#if XNWIN
void CXnCan::printLastErr()
{
	VCI_ERR_INFO errInfo;
	memset(&errInfo, 0, sizeof(VCI_ERR_INFO));

	if(1 == m_pVCI_ReadErrInfo(m_iDevtype, m_dwDevind, 0, &errInfo))
	{
		printf("CXnCan - Last Error:%d.\n", errInfo.ErrCode);	
	}
	else
	{
		printf("CXnCan - Get Last Error FAILED!\n");	
	}
}
#endif

#ifdef XNLINUX
int CXnCan::select(int iTimeout)
{
	struct timeval timout;
	fd_set wset,rset,eset;
	int    ret,maxfdp1;

	if(iTimeout <= 0) return 1;
	/* setup sockets to read */
	maxfdp1 = m_fdSocketCan + 1;
	FD_ZERO(&rset);
	FD_SET (m_fdSocketCan, &rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);
	timout.tv_sec  =  iTimeout / 1000;
	timout.tv_usec = (iTimeout % 1000) * 1000;

	ret = ::select(maxfdp1, &rset ,&wset, &eset, &timout);
	if(ret == 0) 
	{
		return 0; /* timeout */
	}

	return 1;
}
#endif
